
//--------------------------------------------------------------------------------------------------------
// Module  : sdcmd_ctrl
// Type    : synthesizable, IP's sub module
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: sdcmd signal control,
//           instantiated by sd_reader
//--------------------------------------------------------------------------------------------------------

module sdcmd_ctrl (
    input  wire        rstn,
    input  wire        clk,
    // SDcard signals (sdclk and sdcmd)
    output reg         sdclk,
    inout              sdcmd,
    // config clk freq
    input  wire [15:0] clkdiv,
    // user input signal
    input  wire        start,
    input  wire [15:0] precnt,
    input  wire [ 5:0] cmd,
    input  wire [31:0] arg,
    // user output signal
    output reg         busy,
    output reg         done,
    output reg         timeout,
    output reg         syntaxe,
    output wire [31:0] resparg
);


  initial {busy, done, timeout, syntaxe} = 0;
  initial sdclk = 1'b0;

  localparam [7:0] TIMEOUT = 8'd250;

  reg sdcmdoe = 1'b0;
  reg sdcmdout = 1'b1;

  // sdcmd tri-state driver
  assign sdcmd = sdcmdoe ? sdcmdout : 1'bz;
  wire sdcmdin = sdcmdoe ? 1'b1 : sdcmd;

  function [6:0] CalcCrc7;
    input [6:0] crc;
    input [0:0] inbit;
    //function automatic logic [6:0] CalcCrc7(input logic [6:0] crc, input logic inbit);
    begin
      CalcCrc7 = ({crc[5:0], crc[6] ^ inbit} ^ {3'b0, crc[6] ^ inbit, 3'b0});
    end
  endfunction

  reg [5:0] req_cmd = 6'd0;  // request[45:40]
  reg [31:0] req_arg = 0;  // request[39: 8]
  reg [6:0] req_crc = 7'd0;  // request[ 7: 1]
  wire [51:0] request = {6'b111101, req_cmd, req_arg, req_crc, 1'b1};

  //struct packed {
  reg resp_st;
  reg [5:0] resp_cmd;
  reg [31:0] resp_arg;
  //} response = 0;

  assign resparg = resp_arg;

  reg [17:0] clkdivr = 18'h3FFFF;
  reg [17:0] clkcnt = 0;
  reg [15:0] cnt1 = 0;
  reg [ 5:0] cnt2 = 6'h3F;
  reg [ 7:0] cnt3 = 0;
  reg [ 7:0] cnt4 = 8'hFF;


  always @(posedge clk or negedge rstn)
    if (~rstn) begin
      {busy, done, timeout, syntaxe} <= 0;
      sdclk <= 1'b0;
      {sdcmdoe, sdcmdout} <= 2'b01;
      {req_cmd, req_arg, req_crc} <= 0;
      {resp_st, resp_cmd, resp_arg} <= 0;
      clkdivr <= 18'h3FFFF;
      clkcnt <= 0;
      cnt1 <= 0;
      cnt2 <= 6'h3F;
      cnt3 <= 0;
      cnt4 <= 8'hFF;
    end else begin
      {done, timeout, syntaxe} <= 0;

      clkcnt <= (clkcnt < {clkdivr[16:0], 1'b1}) ? (clkcnt + 18'd1) : 18'd0;

      if (clkcnt == 18'd0) clkdivr <= {2'h0, clkdiv} + 18'd1;

      if (clkcnt == clkdivr) sdclk <= 1'b0;
      else if (clkcnt == {clkdivr[16:0], 1'b1}) sdclk <= 1'b1;

      if (~busy) begin
        if (start) busy <= 1'b1;
        req_cmd <= cmd;
        req_arg <= arg;
        req_crc <= 0;
        cnt1 <= precnt;
        cnt2 <= 6'd51;
        cnt3 <= TIMEOUT;
        cnt4 <= 8'd134;
      end else if (done) begin
        busy <= 1'b0;
      end else if (clkcnt == clkdivr) begin
        {sdcmdoe, sdcmdout} <= 2'b01;
        if (cnt1 != 16'd0) begin
          cnt1 <= cnt1 - 16'd1;
        end else if (cnt2 != 6'h3F) begin
          cnt2 <= cnt2 - 6'd1;
          {sdcmdoe, sdcmdout} <= {1'b1, request[cnt2]};
          if (cnt2 >= 8 && cnt2 < 48) req_crc <= CalcCrc7(req_crc, request[cnt2]);
        end
      end else if (clkcnt == {clkdivr[16:0], 1'b1} && cnt1 == 16'd0 && cnt2 == 6'h3F) begin
        if (cnt3 != 8'd0) begin
          cnt3 <= cnt3 - 8'd1;
          if (~sdcmdin) cnt3 <= 8'd0;
          else if (cnt3 == 8'd1) {done, timeout, syntaxe} <= 3'b110;
        end else if (cnt4 != 8'hFF) begin
          cnt4 <= cnt4 - 8'd1;
          if (cnt4 >= 8'd96) {resp_st, resp_cmd, resp_arg} <= {resp_cmd, resp_arg, sdcmdin};
          if (cnt4 == 8'd0) begin
            {done, timeout} <= 2'b10;
            syntaxe <= resp_st || ((resp_cmd!=req_cmd) && (resp_cmd!=6'h3F) && (resp_cmd!=6'd0));
          end
        end
      end
    end

endmodule
